﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;

using ET;
using Marx.Utils;

namespace Marx
{
    [ObjectSystem]
    public class PlayerAwakeSystem : AwakeSystem<Player>
    {
        public override void Awake(Player self)
        {
            self.Awake();
        }
    }

    [ObjectSystem]
    public class PlayerDestroySystem : DestroySystem<Player>
    {
        public override void Destroy(Player self)
        {
            self.Destroy();
        }
    }

    public class Player : Entity, IAwake, IDestroy
    {
        public static Player Instance { get; private set; }

        public const int kLogicZoneId = 1;

        public Session Session => session_;
        private Session session_;

        public string Account => account_;

        private int port_;
        private string ip_;
        private string account_;
        private string password_;

        public static async ETTask<int> Login(string account, string password)
        {
            Host.ClearAllZones();

            try
            {
                var zone = Host.CreateZone(kLogicZoneId, "Logic");
                string ip = PlayerPrefsComponent.Instance.ServerIP;
                int port = PlayerPrefsComponent.Instance.ServerPort;
                var resp = await LoginRealm(zone, ip, port, account, password);
                if(resp == null || resp.Error != ResultCode.OK)
                {
                    return resp == null ? ResultCode.Fail : resp.Error;
                }

                long playerId = resp.Players[0];
                var player = zone.AddChildWithId<Player>(playerId);
                player.account_ = account;
                player.password_ = password;
                player.ip_ = ip;
                player.port_ = port;

                var result =  await player.LoginGate(resp.Address, resp.GateId, resp.Token);
                if(result != ResultCode.OK)
                {
                    player.Dispose();
                }
                return result;
            }
            catch(Exception e)
            {
                Log.Error(e);
                Log.Error("服务器连接失败");
                return ResultCode.Fail;
            }
        }

        public static async ETTask<LoginResponse> LoginRealm(Scene zone, string ip, int port, string account, string password)
        {
            var realmSession = zone.CreateSession(ip, port);
            var resp = await realmSession.Call(new LoginRequest() { Account = account, Password = password, Zone = 1 }) as LoginResponse;
            realmSession.Dispose();
            return resp;
        }

        private async ETTask<int> LoginGate(string address, int gate, long token)
        {
            int index = address.LastIndexOf(':');
            string ip = address.Substring(0, index);
            string p = address.Substring(index + 1);
            int port = int.Parse(p);

            var zone = Host.GetZone(kLogicZoneId);
            session_ = zone.CreateSession(ip, port);

            var resp = await Call<LoginGateResponse>(new LoginGateRequest()
            {
                Account = account_,
                GateId = gate,
                PlayerId = Id,
                Token = token,
            });
            if(resp == null || resp.Error != ResultCode.OK)
            {
                session_.Dispose();
                session_ = null;
                return resp == null ? ResultCode.Error : resp.Error;
            }

            OnLoginSuccess();
            return ResultCode.OK;
        }

        public async ETTask Relogin()
        {
            try
            {
                var resp = await LoginRealm(this.ZoneScene(), ip_, port_, account_, password_);
                if (resp.Error != ResultCode.OK)
                {
                    Game.EventSystem.Publish(new EventType.FatalAndLogout());
                    return;
                }

                var result = await LoginGate(resp.Address, resp.GateId, resp.Token);
                if(result != ResultCode.OK)
                {
                    Game.EventSystem.Publish(new EventType.FatalAndLogout());
                    return;
                }
            }
            catch(Exception e)
            {
                Log.Error(e);
                Game.EventSystem.Publish(new EventType.LostConnection() { Player = this });
            }
        }

        private void OnLoginSuccess()
        {
            session_.AddComponent<PlayerSessionMonitor>();
        }

        public static void Logout()
        {
            Host.ClearAllZones();
            Instance = null;

            UIManager.Instance.CloseAll(false, true);
            UIManager.Instance.Show(UILogin.kName);
        }

        public void Awake()
        {
            Instance = this;
        }

        public void Send(IMessage message)
        {
            if (session_ != null && !session_.IsDisposed)
            {
                session_.Send(message);
            }
        }

        public async ETTask<T> Call<T>(IRequest request) where T : class, IResponse
        {
            var resp = await CallWithoutTips<T>(request);
            if (resp == null)
            {
                UIManager.Instance.ShowErrorTipsContent("No Gate Connections");
            }
            else if (resp.Error != ResultCode.OK)
            {
                UIManager.Instance.ShowErrorTips(resp.Message);
                Log.Debug($"Gate消息返回错误码：{resp.Error}, 错误信息：{resp.Message}", LogColor.Red);
            }
            return resp;
        }

        public async ETTask<T> CallWithoutTips<T>(IRequest request) where T : class, IResponse
        {
            if (session_ != null && !session_.IsDisposed)
            {
                return await session_.Call(request) as T;
            }
            return default(T);
        }

        public void Destroy()
        {
            Instance = null;
            if(session_ != null && !session_.IsDisposed)
            {
                session_.Dispose();
            }
            session_ = null;
        }
    }
}
